﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Browser;
using System.Collections.Generic;
using System.Windows.Controls.Primitives;
using System.Linq;
using System.Xml.Linq;
using System.Text;

namespace Silverlight.ControlToolkit
{

    public enum Corner
    {
        LeftTop,
        LeftCenter,
        LeftBottom,
        RightTop,
        RightCenter,
        RightBottom
    }



    public static class Utilities
    {
        public static bool IsNumeric(string value)
        {
            if (value == null)
                return false;

            value = value.Trim();
            double result = 0;
            return double.TryParse(value, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out result);
        }

        public static bool IsDate(string value)
        {
            if (value == null)
                return false;

            value = value.Trim();
            DateTime result = new DateTime();
            return DateTime.TryParse(value, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out result);
        }

        private static Dictionary<string, Style> _defaultStyles = new Dictionary<string,Style>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="control"></param>
        public static void ApplyDefaultStyleTemplate(this Control control)
        {
            control.ApplyDefaultStyleTemplate(null, null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="control"></param>
        /// <param name="defaultStyle"></param>
        /// <param name="defaultTemplate"></param>
        public static void ApplyDefaultStyleTemplate(this Control control, Style defaultStyle, ControlTemplate defaultTemplate)
        {
            if (defaultStyle == null)
                defaultStyle = Utilities.GetDefaultStyle(control.GetType());

            control.Style = defaultStyle;

            if (control.Template == null && defaultTemplate == null)
            {
                //Read the template from the DefaultStyle read from generic.xaml (this condition is met when user passes a style w/o template)
                foreach (SetterBase curSetter in defaultStyle.Setters)
                {
                    if (curSetter is Setter && ((Setter)curSetter).Value is ControlTemplate)
                    {
                        defaultTemplate = ((Setter)curSetter).Value as ControlTemplate;
                        break;
                    }
                }
            }

            if (defaultTemplate != null)
                control.Template = defaultTemplate;

            control.ApplyTemplate();
        
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="targetType"></param>
        /// <returns></returns>
        public static Style GetDefaultStyle(Type targetType)
        {

            if (_defaultStyles.ContainsKey(targetType.Name))
                return _defaultStyles[targetType.Name];


            XDocument doc = null;
            //Load the enitre generic XML
            using (System.IO.Stream stm = System.Windows.Application.GetResourceStream(new System.Uri("/Silverlight.ControlToolkit;component/Themes/generic.xaml", System.UriKind.Relative)).Stream)
            {
                doc = XDocument.Load(stm);
            }

            //Get the XML Element with the style required
            XElement xelResult = (from p in doc.Root.Elements()
                                  where (p.Attribute("TargetType") != null && (p.Attribute("TargetType").Value == targetType.Name ||
                                                                               p.Attribute("TargetType").Value.EndsWith(":" + targetType.Name)))
                                  select p).SingleOrDefault();


            //Add the namespace to this specific style as we are not loading the entire XML in generic.xaml
            foreach (XAttribute xatCurrent in doc.Root.Attributes())
                xelResult.SetAttributeValue(xatCurrent.Name, xatCurrent.Value);

            string elementXml = xelResult.ToString(SaveOptions.OmitDuplicateNamespaces);

            Style result = System.Windows.Markup.XamlReader.Load(elementXml) as Style;

            //Add it to cache
            _defaultStyles.Add(targetType.Name, result);

            return result;

        }

        public static void ApplyDefaultStyleTemplate(this Control control, string key)
        {
            control.ApplyDefaultStyleTemplate(key, null, null);
        }

        public static void ApplyDefaultStyleTemplate(this Control control, string key, Style defaultStyle, ControlTemplate defaultTemplate)
        {
            if (defaultStyle == null)
                defaultStyle = Utilities.GetDefaultStyle(key);

            control.Style = defaultStyle;

            if (control.Template == null && defaultTemplate == null)
            {
                //Read the template from the DefaultStyle read from generic.xaml (this condition is met when user passes a style w/o template)
                foreach (SetterBase curSetter in defaultStyle.Setters)
                {
                    if (curSetter is Setter && ((Setter)curSetter).Value is ControlTemplate)
                    {
                        defaultTemplate = ((Setter)curSetter).Value as ControlTemplate;
                        break;
                    }
                }
            }

            if (defaultTemplate != null)
                control.Template = defaultTemplate;

            control.ApplyTemplate();

        }

        public static Style GetDefaultStyle(string key)
        {

            if (_defaultStyles.ContainsKey(key))
                return _defaultStyles[key];


            XDocument doc = null;
            //Load the enitre generic XML
            using (System.IO.Stream stm = System.Windows.Application.GetResourceStream(new System.Uri("/Silverlight.ControlToolkit;component/Themes/generic.xaml", System.UriKind.Relative)).Stream)
            {
                doc = XDocument.Load(stm);
            }

            //Get the XML Element with the style required
            XElement xelResult = (from p in doc.Root.Elements()
                                  where (p.FirstAttribute != null && p.FirstAttribute.Name.LocalName == "Key" && p.FirstAttribute.Value == key)
                                  select p).SingleOrDefault();


            //Add the namespace to this specific style as we are not loading the entire XML in generic.xaml
            foreach (XAttribute xatCurrent in doc.Root.Attributes())
                xelResult.SetAttributeValue(xatCurrent.Name, xatCurrent.Value);

            string elementXml = xelResult.ToString(SaveOptions.OmitDuplicateNamespaces);

            Style result = System.Windows.Markup.XamlReader.Load(elementXml) as Style;

            //Add it to cache
            _defaultStyles.Add(key, result);

            return result;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public static Point GetPosition(this UIElement element)
        {
            GeneralTransform gt = element.TransformToVisual(Application.Current.RootVisual as UIElement);
            return gt.Transform(new Point(0, 0));
        }

        public static Point GetPosition(this FrameworkElement e, Corner p)
        {
            GeneralTransform gt = e.TransformToVisual(Application.Current.RootVisual as UIElement);
            Point po = new Point();
            if (p == Corner.LeftTop)
                po = gt.Transform(new Point(0, 0));
            else if (p == Corner.LeftCenter)
                po = gt.Transform(new Point(0, e.ActualHeight/2));
            else if (p == Corner.LeftBottom)
                po = gt.Transform(new Point(0, e.ActualHeight));
            else if (p == Corner.RightTop)
                po = gt.Transform(new Point(e.ActualWidth, 0));
            else if (p == Corner.RightCenter)
                po = gt.Transform(new Point(e.ActualWidth, e.ActualHeight/2));
            else if (p == Corner.RightBottom)
                po = gt.Transform(new Point(e.ActualWidth, e.ActualHeight));
            return po;
        }

        public static Point GetCenterPosition(this FrameworkElement child, FrameworkElement parent)
        {
            double horizontalOffset = 0;
            double verticalOffset = 0;
            double parentWidth = 0;
            double parentHeight = 0;

            if (parent != null)
            {
                Point parentPosition;
                
                if (child.Parent != null && child.Parent.Equals(parent))
                    parentPosition = new Point();
                else
                    parentPosition= parent.GetPosition();

                horizontalOffset = parentPosition.X;
                verticalOffset = parentPosition.Y;
                parentWidth = parent.ActualWidth == 0 ? parent.Width : parent.ActualWidth;
                parentHeight = parent.ActualHeight == 0 ? parent.Height : parent.ActualHeight;
            }
            else
            {
                FrameworkElement objRoot = Application.Current.RootVisual as FrameworkElement;
                parentWidth = objRoot.ActualWidth == 0 ? objRoot.Width : objRoot.ActualWidth;
                parentHeight = objRoot.ActualHeight == 0 ? objRoot.Height : objRoot.ActualHeight;
            }

            Point result = new Point();

            result.X = ((parentWidth - (child.ActualWidth == 0 ? child.Width : child.ActualWidth)) / 2);
            result.Y = ((parentHeight - (child.ActualHeight == 0 ? child.Height : child.ActualHeight)) / 2);

            if (child is Popup)
            {
                result.X += horizontalOffset;
                result.Y += verticalOffset;
            }


            return result;
        }

        public static Point GetCenterPosition(this FrameworkElement child)
        {
            return GetCenterPosition(child, null);
        }


        public static void PlaceInCenter(this Popup child, FrameworkElement parent)
        {
            Point result = GetCenterPosition(child.Child as FrameworkElement, parent);
            child.HorizontalOffset = result.X;
            child.VerticalOffset = result.Y;
        }

        public static void PlaceInCenter(this FrameworkElement child, FrameworkElement parent)
        {
            Point result = GetCenterPosition(child, parent);
            child.SetValue(Canvas.LeftProperty, result.X);
            child.SetValue(Canvas.TopProperty, result.Y);
        }

        public static void PlaceInCenter(this FrameworkElement child)
        {
            PlaceInCenter(child, null);
        }

        public static void PlaceBesidesOwner(this Popup child, FrameworkElement owner, Corner corner, Thickness margin)
        {
            FrameworkElement objRoot = Application.Current.RootVisual as FrameworkElement;
            if (owner != objRoot)
            {
                Point parentPosition = owner.GetPosition(corner);

                child.Margin = margin;
                child.HorizontalOffset = parentPosition.X;
                if ((corner == Corner.LeftCenter || corner == Corner.RightCenter) && child.Child is FrameworkElement)
                {
                    child.VerticalOffset = parentPosition.Y  - (((FrameworkElement) child.Child).ActualHeight / 2) ;
                }
                else
                    child.VerticalOffset = parentPosition.Y;
            }
        }

        public static void PlaceInsideOwner(this Popup child, FrameworkElement owner)
        {
            if ((child.Child as FrameworkElement).Width != owner.ActualWidth)
                (child.Child as FrameworkElement).Width = owner.ActualWidth;

            if ((child.Child as FrameworkElement).Height != owner.ActualHeight)
                (child.Child as FrameworkElement).Height = owner.ActualHeight;

            FrameworkElement objRoot = Application.Current.RootVisual as FrameworkElement;
            if (owner != objRoot)
            {
                Point parentPosition = owner.GetPosition();
                child.HorizontalOffset = parentPosition.X;
                child.VerticalOffset = parentPosition.Y;
            }
        }

        public static void PlaceInsideOwner(this FrameworkElement child, FrameworkElement owner)
        {
            if (child.Width != owner.ActualWidth)
                child.Width = owner.ActualWidth;

            if (child.Height != owner.ActualHeight)
                child.Height = owner.ActualHeight;

            FrameworkElement objRoot = Application.Current.RootVisual as FrameworkElement;
            if (owner != objRoot)
            {
                Point parentPosition = owner.GetPosition();
                child.SetValue(Canvas.LeftProperty, parentPosition.X);
                child.SetValue(Canvas.TopProperty, parentPosition.Y);
            }
        }

        public static Panel GetRootPanel()
        {
            FrameworkElement objRoot = Application.Current.RootVisual as FrameworkElement;

            if (objRoot is Panel)
                return objRoot as Panel;

            for (int intChildIndex = 0; intChildIndex < VisualTreeHelper.GetChildrenCount(objRoot); intChildIndex++)
            {
                DependencyObject objResults = VisualTreeHelper.GetChild(objRoot, intChildIndex);
                if (objResults is Panel)
                    return objResults as Panel;
            }

            return null;

        }

        public static Panel GetPanelFromParents(FrameworkElement startingElement)
        {
            FrameworkElement currentElement = startingElement as FrameworkElement ;

            while (currentElement != null)
            {
                if (currentElement is Panel)
                    return currentElement as Panel;

                currentElement = currentElement.Parent as FrameworkElement;
            }

            //Not found yet. Pass the root
            return GetRootPanel();
            
        }


        public static IList<FrameworkElement> GetAllParents(this FrameworkElement elem)
        {
            List<FrameworkElement> list = new List<FrameworkElement>();
            if (elem != null)
            {
                FrameworkElement item = elem;
                while (item.Parent is FrameworkElement)
                {
                    item = (FrameworkElement)item.Parent;
                    list.Add(item);
                }
            }
            return list;
        }


        // Methods
        public static FrameworkElement GetChildByName(this FrameworkElement parent, string name)
        {
            if (parent.Name == name)
            {
                return parent;
            }
            if ((TypeMatch(parent.GetType(), typeof(ContentPresenter)) && (((ContentPresenter)parent).Content != null)) && TypeMatch(((ContentPresenter)parent).Content.GetType(), typeof(FrameworkElement)))
            {
                return ((FrameworkElement)((ContentPresenter)parent).Content).GetChildByName(name);
            }
            if ((TypeMatch(parent.GetType(), typeof(ContentControl)) && (((ContentControl)parent).Content != null)) && TypeMatch(((ContentControl)parent).Content.GetType(), typeof(FrameworkElement)))
            {
                return ((FrameworkElement)((ContentPresenter)parent).Content).GetChildByName(name);
            }
            if (TypeMatch(parent.GetType(), typeof(Panel)))
            {
                foreach (UIElement element in ((Panel)parent).Children)
                {
                    if (((FrameworkElement)element).Name == name)
                    {
                        return (FrameworkElement)element;
                    }
                    FrameworkElement childByName = ((FrameworkElement)element).GetChildByName(name);
                    if (childByName != null)
                    {
                        return childByName;
                    }
                }
            }
            return null;
        }

        public static FrameworkElement GetChildByType(this FrameworkElement parent, Type childType)
        {

            foreach (FrameworkElement currentChild in GetVisualChildren(parent).OfType<FrameworkElement>())
            {
                if (TypeMatch(currentChild.GetType(), childType))
                    return currentChild;
                else
                {
                    FrameworkElement newChild = GetChildByType(currentChild, childType);

                    if (newChild != null)
                        return newChild;
                }
            }

            return null;
        }

        public static FrameworkElement GetParentByType(this FrameworkElement child, Type parentType)
        {
            FrameworkElement currentElement = child.Parent as FrameworkElement;
            while (currentElement != null)
            {
                if (TypeMatch(currentElement.GetType(), parentType))
                    return currentElement;

                currentElement = currentElement.Parent as FrameworkElement;
            }

            return null;
        }

        public static bool TypeMatch(this Type type1, Type type2)
        {
            
            return ((((type1 == type2) || HasInterface(type1, type2)) || HasInterface(type2, type1)) || ((type1.BaseType != null) && TypeMatch(type1.BaseType, type2)));
        }

        public static bool HasInterface(this Type type, Type iface)
        {
            foreach (Type type2 in type.GetInterfaces())
            {
                if (type2 == iface)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Retrieves all the visual children of a framework element.
        /// </summary>
        /// <param name="parent">The parent framework element.</param>
        /// <returns>The visual children of the framework element.</returns>
        internal static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject parent)
        {

            int childCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int counter = 0; counter < childCount; counter++)
            {
                yield return VisualTreeHelper.GetChild(parent, counter);
            }
        }

    }//end of class

}
